package de.fu_berlin.minesweeper;

import java.util.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

/**
 * Field class, TASK 3b
 */
public class Field extends JPanel{
	
	private final int COLUMNS, ROWS;	// rows and columns
	private float fProbability;			// probability of a field being a bomb
	private Position[][] arrayField;	// field
	private final JToggleButton[][] arrayBtn;	// matrix of buttons for the ui
	private static Random objRnd = new Random();
	private ArrayList<GameListener> lsListeners = new ArrayList<GameListener>(); // listener list
	private int intRemainingFields = 0;
	
	private int intLastValue;			// last value clicked
										// -1 if it is a bomb
	
	/**
	 * Default constructor, TASK 3a
	 */
	public Field(){
		this(16, 16, 0.3f);
	}
	
	public Field(int intColumns, int intRows, float fProbability){
		this.COLUMNS = intColumns;
		this.ROWS 	 = intRows;
		this.arrayField 	= new Position[this.ROWS][this.COLUMNS];
		this.arrayBtn		= new JToggleButton[this.ROWS][this.COLUMNS];
		this.fProbability	= fProbability;
		this.intRemainingFields = this.COLUMNS * this.ROWS;
		this.init();
	}
	
	/**
	 * Initialize field and UI
	 */
	public void init(){
		
		// generate field
		for (int i = 0; i < this.ROWS; i++){
			for (int j = 0; j < this.COLUMNS; j++){
				this.arrayField[i][j] = new Position(
										(Field.objRnd.nextFloat() <= this.fProbability) ? true : false);
			}
		}
		
		this.generateSolution();	// generating solution
		this.initUI();				// init user interface
	}
	/**
	 * Returns the amount of fields not played yet
	 * @return integer value
	 */
	public int getRemainingFields(){
		return this.intRemainingFields;
	}
	private void initUI(){
		this.setLayout(new GridLayout(this.ROWS,this.COLUMNS));
		for (int iRow = 0; iRow < this.ROWS; iRow++){
			for (int jCol = 0; jCol < this.COLUMNS; jCol++){
				
				JToggleButton objBtn = new JToggleButton(this.arrayField[iRow][jCol].toString());
				
				// set data associated with the button row#col
				objBtn.setName(Integer.toString(iRow) + "#" +  Integer.toString(jCol));
				
				// add event listener
				objBtn.addActionListener(new ActionListener(){
					@Override public void actionPerformed(ActionEvent e){
						// reset last clicked value
						intLastValue = 0;
						
						// get additional data
						String[] lsStrPos = ((JToggleButton)e.getSource())
												.getName().split("#");
						
						// reveal cell in matrix
						revealCell(Integer.parseInt(lsStrPos[0]),
								   Integer.parseInt(lsStrPos[1]),
								   false);
						
						//notify everybody who listens: remaining fields changed
						for (GameListener objListener : lsListeners){
							objListener.remainingFieldsChanged(intRemainingFields);
							objListener.gainedPoints(intLastValue);
						}

						// leave button in a pressed state
						((JToggleButton)e.getSource()).setSelected(true);	
						
						if (intRemainingFields == 0){ // all fields played?
							//notify everybody who listens: game won
							for (GameListener objListener : lsListeners){
								objListener.gameWon();
							
							}
						}
					}
				});
				this.arrayBtn[iRow][jCol] = objBtn;
				this.add(objBtn);
			}
		}	
	}
	
	public void clear(){
		
	}
	
	/**
	 * Calculates the amount of bombs in the neighbourhood
	 * @param i, i-th row
	 * @param j, j-th column
	 * @return amount of bombs in the direct neighbourhood of (i,j)
	 */
	private int calculateNeighbours(int i, int j){
		int intCounter = 0;
		
		// checks the direct neighbourhood
		for (int r = i - 1; r <= i + 1; r++){
			if (r >= 0 && r < this.ROWS){
				for (int s = j - 1; s <= j + 1; s++){
					if (s >= 0 && s < this.COLUMNS){
						if (this.arrayField[r][s].isBomb())
							intCounter++;
					}
				}
			}
		}
		return intCounter;
	}
	
	/**
	 * Shows the solution in the UI
	 */
	private void showSolution(){
		for (int i = 0; i < this.ROWS; i++){
			for (int j = 0; j < this.COLUMNS; j++){
				this.revealCell(i,j, true);
			}
		}
	}
	
	/**
	 * Reveals the cell and all possible adjoining non-bomb cells
	 * @param row
	 * @param col
	 * @param bShowAll, showing the solution or not
	 */
	private void revealCell(int row, int col, boolean bShowAll){
		if (!this.arrayField[row][col].revealed()){
			
			this.arrayField[row][col].reveal();
			if (!bShowAll)
				this.intRemainingFields--;	// decrease remaining fields
			
			// set new text of field
			this.arrayBtn[row][col].setText(this.arrayField[row][col].toString());
			
			// toggle button
			this.arrayBtn[row][col].setSelected(true);
			
			if (!bShowAll){
				
				//increase lastValue
				this.intLastValue += this.arrayField[row][col].getBombNeighbours();
				
				if (this.arrayField[row][col].isBomb()){
					
					this.arrayBtn[row][col].setForeground(Color.RED); // bomb color
					
					this.intLastValue = -1;
					
					// show solution
					this.showSolution();
					
					// indicates game over by discovering a bomb
					this.intRemainingFields = -1;
					
					// notify everybody who listens: game over
					for (GameListener objListener : lsListeners){
						objListener.gameOver();
					}

				}else if(this.arrayField[row][col].getBombNeighbours() == 0){
					// field has no bombs in the direct neighbourhood
					for (int r = row - 1; r <= row + 1; r++){
						if (r >= 0 && r < this.ROWS){
							for (int s = col - 1; s <= col + 1; s++){
								if (s >= 0 && s < this.COLUMNS){
									this.revealCell(r, s, bShowAll);
								}
							}
						}
					}
				}
			}
		}
	}
	
	/**
	 * Generates the solution to the current field
	 */
	public void generateSolution(){
		for (int i = 0; i < this.ROWS; i++){
			for (int j = 0; j < this.COLUMNS; j++){
				this.arrayField[i][j].setBombNeighbours(this.calculateNeighbours(i, j));
				if (this.arrayField[i][j].isBomb())
					this.intRemainingFields--;
			}
		}
	}
	
	/**
	 * Adds a listener
	 * @param objListener - listening object
	 */
	public void addGameListener(GameListener objListener){
		lsListeners.add(objListener);
	}
	/**
	 * Reveals the (row,col) field by clicking on it
	 * @param row
	 * @param col
	 * @return points gained by clicking the field, -1 if it was a bomb
	 */
	protected int chooseField(int row, int col){
		this.arrayBtn[row][col].doClick();
		return this.intLastValue;
	}
}
